home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 1998 Aladdin Enterprises. All rights reserved.
-
- This file is part of AFPL Ghostscript.
-
- AFPL Ghostscript is distributed with NO WARRANTY OF ANY KIND. No author or
- distributor accepts any responsibility for the consequences of using it, or
- for whether it serves any particular purpose or works at all, unless he or
- she says so in writing. Refer to the Aladdin Free Public License (the
- "License") for full details.
-
- Every copy of AFPL Ghostscript must include a copy of the License, normally
- in a plain ASCII text file named PUBLIC. The License grants you the right
- to copy, modify and redistribute AFPL Ghostscript, but only under certain
- conditions described in the License. Among other things, the License
- requires that the copyright notice and this notice be preserved on all
- copies.
- */
-
- /*$Id: gxpageq.c,v 1.2 2000/09/19 19:00:39 lpd Exp $ */
- /* Page queue implementation */
-
- /* Initial version 2/1/98 by John Desrosiers (soho@crl.com) */
-
- #include "gx.h"
- #include "gxdevice.h"
- #include "gxclist.h"
- #include "gxpageq.h"
- #include "gserrors.h"
- #include "gsstruct.h"
-
- /* Define the structure implementation for a page queue. */
- struct gx_page_queue_s {
- gs_memory_t *memory; /* allocator used to allocate entries */
- gx_monitor_t *monitor; /* used to serialize access to this structure */
- int entry_count; /* # elements in page_queue */
- bool dequeue_in_progress; /* true between start/ & end_dequeue */
- gx_semaphore_t *render_req_sema; /* sema signalled when page queued */
- bool enable_render_done_signal; /* enable signals to render_done_sema */
- gx_semaphore_t *render_done_sema; /* semaphore signaled when (partial) page rendered */
- gx_page_queue_entry_t *last_in; /* if <> 0, Last-in queue entry */
- gx_page_queue_entry_t *first_in; /* if <> 0, First-in queue entry */
- gx_page_queue_entry_t *reserve_entry; /* spare allocation */
- };
-
- /*
- * Null initializer for entry page_info (used by gx_page_queue_add_page() ).
- */
- private const gx_band_page_info_t null_page_info = { PAGE_INFO_NULL_VALUES };
-
- #define private_st_gx_page_queue()\
- gs_private_st_ptrs4(st_gx_page_queue, gx_page_queue_t, "gx_page_queue",\
- gx_page_queue_enum_ptrs, gx_page_queue_reloc_ptrs,\
- monitor, first_in, last_in, reserve_entry);
-
- /* ------------------- Global Data ----------------------- */
-
- /* Structure descriptor for GC */
- private_st_gx_page_queue_entry();
- private_st_gx_page_queue();
-
- /* ------------ Forward Decl's --------------------------- */
- private gx_page_queue_entry_t * /* removed entry, 0 if none avail */
- gx_page_queue_remove_first(P1(
- gx_page_queue_t * queue /* page queue to retrieve from */
- ));
-
-
- /* --------------------Procedures------------------------- */
-
- /* Allocate a page queue. */
- gx_page_queue_t *
- gx_page_queue_alloc(gs_memory_t *mem)
- {
- return gs_alloc_struct(mem, gx_page_queue_t, &st_gx_page_queue,
- "gx_page_queue_alloc");
- }
-
- /* ------- page_queue_entry alloc/free --------- */
-
- /* Allocate & init a gx_page_queue_entry */
- gx_page_queue_entry_t * /* rets ptr to allocated object, 0 if VM error */
- gx_page_queue_entry_alloc(
- gx_page_queue_t * queue /* queue that entry is being alloc'd for */
- )
- {
- gx_page_queue_entry_t *entry
- = gs_alloc_struct(queue->memory, gx_page_queue_entry_t,
- &st_gx_page_queue_entry, "gx_page_queue_entry_alloc");
-
- if (entry != 0) {
- entry->next = 0;
- entry->queue = queue;
- }
- return entry;
- }
-
- /* Free a gx_page_queue_entry allocated w/gx_page_queue_entry_alloc */
- void
- gx_page_queue_entry_free(
- gx_page_queue_entry_t * entry /* entry to free up */
- )
- {
- gs_free_object(entry->queue->memory, entry, "gx_page_queue_entry_free");
- }
-
- /* Free the clist resources held by a gx_page_queue_entry_t */
- void
- gx_page_queue_entry_free_page_info(
- gx_page_queue_entry_t * entry /* entry to free up */
- )
- {
- clist_close_page_info( &entry->page_info );
- }
-
- /* -------- page_queue init/dnit ---------- */
-
- /* Initialize a gx_page_queue object */
- int /* -ve error code, or 0 */
- gx_page_queue_init(
- gx_page_queue_t * queue, /* page queue to init */
- gs_memory_t * memory /* allocator for dynamic memory */
- )
- {
- queue->memory = memory;
- queue->monitor = gx_monitor_alloc(memory); /* alloc monitor to serialize */
- queue->entry_count = 0;
- queue->dequeue_in_progress = false;
- queue->render_req_sema = gx_semaphore_alloc(memory);
- queue->enable_render_done_signal = false;
- queue->render_done_sema = gx_semaphore_alloc(memory);
- queue->first_in = queue->last_in = 0;
- queue->reserve_entry = gx_page_queue_entry_alloc(queue);
-
- if (queue->monitor && queue->render_req_sema && queue->render_done_sema
- && queue->reserve_entry)
- return 0;
- else {
- gx_page_queue_dnit(queue);
- return gs_error_VMerror;
- }
- }
-
- /* Dnitialize a gx_page_queue object */
- void
- gx_page_queue_dnit(
- gx_page_queue_t * queue /* page queue to dnit */
- )
- {
- /* Deallocate any left-over queue entries */
- gx_page_queue_entry_t *entry;
-
- while ((entry = gx_page_queue_remove_first(queue)) != 0) {
- gx_page_queue_entry_free_page_info(entry);
- gx_page_queue_entry_free(entry);
- }
-
- /* Free dynamic objects */
- if (queue->monitor) {
- gx_monitor_free(queue->monitor);
- queue->monitor = 0;
- }
- if (queue->render_req_sema) {
- gx_semaphore_free(queue->render_req_sema);
- queue->render_req_sema = 0;
- }
- if (queue->render_done_sema) {
- gx_semaphore_free(queue->render_done_sema);
- queue->render_done_sema = 0;
- }
- if (queue->reserve_entry) {
- gx_page_queue_entry_free(queue->reserve_entry);
- queue->reserve_entry = 0;
- }
- }
-
- /* -------- low-level queue add/remove ---------- */
-
- /* Retrieve & remove firstin queue entry */
- private gx_page_queue_entry_t * /* removed entry, 0 if none avail */
- gx_page_queue_remove_first(
- gx_page_queue_t * queue /* page queue to retrieve from */
- )
- {
- gx_page_queue_entry_t *entry = 0; /* assume failure */
-
- /* Enter monitor */
- gx_monitor_enter(queue->monitor);
-
- /* Get the goods */
- if (queue->entry_count) {
- entry = queue->first_in;
- queue->first_in = entry->next;
- if (queue->last_in == entry)
- queue->last_in = 0;
- --queue->entry_count;
- }
- /* exit monitor */
- gx_monitor_leave(queue->monitor);
-
- return entry;
- }
-
- /* Add entry to queue at end */
- private void
- gx_page_queue_add_last(
- gx_page_queue_entry_t * entry /* entry to add */
- )
- {
- gx_page_queue_t *queue = entry->queue;
-
- /* Enter monitor */
- gx_monitor_enter(queue->monitor);
-
- /* Add the goods */
- entry->next = 0;
- if (queue->last_in != 0)
- queue->last_in->next = entry;
- queue->last_in = entry;
- if (queue->first_in == 0)
- queue->first_in = entry;
- ++queue->entry_count;
-
- /* exit monitor */
- gx_monitor_leave(queue->monitor);
- }
-
- /* --------- low-level synchronization ---------- */
-
- /* Wait for a single page to finish rendering (if any pending) */
- int /* rets 0 if no pages were waiting for rendering, 1 if actually waited */
- gx_page_queue_wait_one_page(
- gx_page_queue_t * queue /* queue to wait on */
- )
- {
- int code;
-
- gx_monitor_enter(queue->monitor);
- if (!queue->entry_count && !queue->dequeue_in_progress) {
- code = 0;
- gx_monitor_leave(queue->monitor);
- } else {
- /* request acknowledgement on render done */
- queue->enable_render_done_signal = true;
-
- /* exit monitor & wait for acknowlegement */
- gx_monitor_leave(queue->monitor);
- gx_semaphore_wait(queue->render_done_sema);
- code = 1;
- }
- return code;
- }
-
- /* Wait for page queue to become empty */
- void
- gx_page_queue_wait_until_empty(
- gx_page_queue_t * queue /* page queue to wait on */
- )
- {
- while (gx_page_queue_wait_one_page(queue));
- }
-
- /* ----------- Synchronized page_queue get/put routines ------ */
-
- /* Add an entry to page queue for rendering w/sync to renderer */
- void
- gx_page_queue_enqueue(
- gx_page_queue_entry_t * entry /* entry to add */
- )
- {
- gx_page_queue_t *queue = entry->queue;
-
- /* Add the goods to queue, & signal it */
- gx_page_queue_add_last(entry);
- gx_semaphore_signal(queue->render_req_sema);
- }
-
- /* Add page to a page queue */
- /* Even if an error is returned, entry will have been added to queue! */
- int /* rets 0 ok, gs_error_VMerror if error */
- gx_page_queue_add_page(
- gx_page_queue_t * queue, /* page queue to add to */
- gx_page_queue_action_t action, /* action code to queue */
- const gx_band_page_info_t * page_info, /* bandinfo incl. bandlist (or 0) */
- int page_count /* see comments in gdevprna.c */
- )
- {
- int code = 0;
-
- /* Allocate a new page queue entry */
- gx_page_queue_entry_t *entry
- = gx_page_queue_entry_alloc(queue);
-
- if (!entry) {
- /* Use reserve page queue entry */
- gx_monitor_enter(queue->monitor); /* not strictly necessary */
- entry = queue->reserve_entry;
- queue->reserve_entry = 0;
- gx_monitor_leave(queue->monitor);
- }
- /* Fill in page queue entry with info from device */
- entry->action = action;
- if (page_info != 0)
- entry->page_info = *page_info;
- else
- entry->page_info = null_page_info;
- entry->num_copies = page_count;
-
- /* Stick onto page queue & signal */
- gx_page_queue_enqueue(entry);
-
- /* If a new reserve entry is needed, wait till enough mem is avail */
- while (!queue->reserve_entry) {
- queue->reserve_entry = gx_page_queue_entry_alloc(queue);
- if (!queue->reserve_entry && !gx_page_queue_wait_one_page(queue)) {
- /* Should never happen: all pages rendered & still can't get memory: give up! */
- code = gs_note_error(gs_error_Fatal);
- break;
- }
- }
- return code;
- }
-
- /* Wait for & get next page queue entry */
- gx_page_queue_entry_t * /* removed entry */
- gx_page_queue_start_dequeue(
- gx_page_queue_t * queue /* page queue to retrieve from */
- )
- {
- gx_semaphore_wait(queue->render_req_sema);
- queue->dequeue_in_progress = true;
- return gx_page_queue_remove_first(queue);
- }
-
- /* After rendering page gotten w/gx_page_queue_dequeue, call this to ack */
- void
- gx_page_queue_finish_dequeue(
- gx_page_queue_entry_t * entry /* entry that was retrieved to delete */
- )
- {
- gx_page_queue_t *queue = entry->queue;
-
- gx_monitor_enter(queue->monitor);
- if (queue->enable_render_done_signal) {
- queue->enable_render_done_signal = false;
- gx_semaphore_signal(queue->render_done_sema);
- }
- queue->dequeue_in_progress = false;
-
- /*
- * Delete the previously-allocated entry, do inside monitor in case
- * this is the reserve entry & is the only memory in the universe;
- * in that case gx_page_queue_add_page won't be looking for this
- * until the monitor is exited.
- * In this implementation of the page queue, clist and queue entries
- * are managed together, so free the clist just before freeing the entry.
- */
- gx_page_queue_entry_free_page_info(entry);
- gx_page_queue_entry_free(entry);
-
- gx_monitor_leave(queue->monitor);
- }
-